{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用量桨通过量易伏平台连接量子计算机\n",
    "\n",
    "*Copyright (c) 2022 Institute for Quantum Computing, Baidu Inc. All Rights Reserved.*\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 简介\n",
    "\n",
    "在量子机器学习中，我们最终想实现的目标就是使用量子计算机和经典计算机进行混合计算，从而实现高效的量子机器学习（Quantum Machine Learning, QML）算法。在量桨（Paddle Quantum）中，我们也支持通过[量易伏](https://quantum-hub.baidu.com/)这一云原生量子计算平台连接到真实的量子计算机，从而使用量子计算机实现 QML 算法。\n",
    "\n",
    "### 量易伏简介\n",
    "\n",
    "量易伏是由百度研究院量子计算研究所推出的云原生量子计算平台。量易伏很好地实现了量子计算和云计算的深度融合。量易包含本地模拟器、云端模拟器和云端量子计算机等多种使用方式。其中，云端模拟器和云端量子计算机的使用需要消耗量易伏点数，用户需要在[量易伏网站](https://quantum-hub.baidu.com/)上进行注册后可获得量易伏账号和点数。量易伏包含 QComposer、PyOnline、YunOnline、QCompute SDK 等多种调用方式。其中 QCompute SDK 允许我们通过 Python 进行调用。在 Python 环境中，需要输入账号对应的 token 来进行调用，token 可在 https://quantum-hub.baidu.com/token 上进行查看。如果想使用云端模拟器或云端量子计算机，需要消耗量易伏点数，点数可在 https://quantum-hub.baidu.com/feedback 页面发送邮件进行获取。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 量桨连接量易伏\n",
    "\n",
    "### 环境设置\n",
    "\n",
    "如果要使用量桨连接量易伏上的真实量子计算机，则需要保证所安装的 qcompute 的版本在 3.0.0 以上，使用 `pip install -U qcompute` 来安装最新版本的 qcompute。\n",
    "\n",
    "其中，qcompute 对 protobuf 的版本要求是 4.21.1，而飞桨对 protobuf 的版本要求为 3.1.0 到3.20.0。因此，需要通过设置环境变量来使飞桨可以兼容更高版本的 protobuf。\n",
    "\n",
    "> 因为飞桨对 protobuf 的版本要求为 3.1.0 到3.20.0，所以所有的 Python 程序都需要先设置环境变量才能正常使用量桨。因此，如果不使用量易伏的话，可以通过 `pip install protobuf==3.20.0` 来正常使用量桨而无需额外设置环境变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 设置环境变量使飞桨可以兼容更高版本的 protobuf\n",
    "import os\n",
    "os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，只需要使用 `paddle_quantum.set_backend('quleaf')` 即可将量桨的后端设置为量易伏。除此之外，我们还需要设置量易伏的模拟方式。如果使用云端算力，则还需要输入 token。因此，完整的设置代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle_quantum\n",
    "from QCompute import BackendName\n",
    "paddle_quantum.set_backend('quleaf')\n",
    "paddle_quantum.backend.quleaf.set_quleaf_backend(BackendName.LocalBaiduSim2)\n",
    "# 如果使用本地模拟器，则可以不需要输入 token\n",
    "# paddle_quantum.backend.quleaf.set_quleaf_token('your token')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 `set_quleaf_backend()` 函数中可以设置量易伏的后端模拟方式。\n",
    "\n",
    "目前，量易伏的所有后端如下：\n",
    "\n",
    "| 类型 | 量子端 | 说明 |\n",
    "| :--: | :--: | :--: |\n",
    "| 本地 | LocalBaiduSim2 | 使用 Python 编写的 Sim2 本地版 |\n",
    "| 云端 | CloudBaiduSim2Water | 使用 C++ 编写的多实例 Sim2 模拟器云版 |\n",
    "| 云端 | CloudBaiduSim2Earth | 使用 Python 编写的单一实例高配置 Sim2 模拟器云版 |\n",
    "| 云端 | CloudBaiduSim2Thunder | 使用 C++ 编写的单一实例高配置 Sim2 模拟器云版 |\n",
    "| 云端 | CloudBaiduSim2Wind | 使用 C++ 编写的单一实例 Sim2 模拟器云版（支持稀疏模式） |\n",
    "| 云端 | CloudBaiduSim2Heaven | 使用 C++ 编写的单一实例集群 Sim2 模拟器云版 |\n",
    "| 云端 | CloudBaiduSim2Lake | 使用 C++ 编写的单一实例 Sim2 模拟器云版 (支持 GPU 运算) |\n",
    "| 云端 | CloudAerAtBD | 开源 Aer(C++ 版) 模拟器云版 |\n",
    "| 云端 | CloudIoPCAS | 来自中科院物理所的 10 比特量子真机 (VIP) |\n",
    "\n",
    "其中，`LocalBaiduSim2` 为本地模拟器，不需要输入 token，不会使用量易伏点数；`CloudIoPCAS` 为真实的量子计算机，云端的量子计算机和模拟器都需要输入token，且会消耗量易伏点数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 贝尔态制备\n",
    "\n",
    "这里，我们以制备贝尔态为例测试量桨是否成功连接量易伏。在执行了上面的代码之后，用户就已经将量桨设置为了量易伏模式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_qubits = 2\n",
    "init_state = paddle_quantum.state.zero_state(num_qubits)\n",
    "circuit = paddle_quantum.ansatz.Circuit(num_qubits)\n",
    "circuit.h(0)\n",
    "circuit.cnot([0, 1])\n",
    "bell_state = circuit(init_state)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果上面的代码能成功运行，则说明可以连接到量易伏，并执行量子电路。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## VQE 例子\n",
    "\n",
    "这里，我们以VQE为例来介绍如何通过量桨调用量易伏算力。我们使用量易伏的本地模拟器来进行 demo 演示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The iter is   0, loss is -0.36935.\n",
      "The iter is  10, loss is -0.51383.\n",
      "The iter is  20, loss is -0.74604.\n",
      "The iter is  30, loss is -1.02815.\n",
      "The iter is  40, loss is -1.07233.\n",
      "The iter is  50, loss is -1.09937.\n",
      "The iter is  60, loss is -1.11564.\n",
      "The iter is  70, loss is -1.11459.\n",
      "The theoretical value is -1.137283834485513.\n"
     ]
    }
   ],
   "source": [
    "import paddle\n",
    "\n",
    "# 定义哈密顿量\n",
    "hamiltonian_list = [\n",
    "    [-0.0970662686176252, 'I'],\n",
    "    [-0.04530261550868938, 'X0, X1, Y2, Y3'],\n",
    "    [0.04530261550868938, 'X0, Y1, Y2, X3'],\n",
    "    [0.04530261550868938, 'Y0, X1, X2, Y3'],\n",
    "    [-0.04530261550868938, 'Y0, Y1, X2, X3'],\n",
    "    [0.1714128263940238, 'Z0'],\n",
    "    [0.16868898168693292, 'Z0, Z1'],\n",
    "    [0.12062523481381847, 'Z0, Z2'],\n",
    "    [0.1659278503225078, 'Z0, Z3'],\n",
    "    [0.17141282639402383, 'Z1'],\n",
    "    [0.1659278503225078, 'Z1, Z2'],\n",
    "    [0.12062523481381847, 'Z1, Z3'],\n",
    "    [-0.22343153674664024, 'Z2'],\n",
    "    [0.17441287610651632, 'Z2, Z3'],\n",
    "    [-0.2234315367466403, 'Z3'],\n",
    "]\n",
    "\n",
    "# 定义电路\n",
    "num_qubits = 4\n",
    "circuit = paddle_quantum.ansatz.Circuit(num_qubits)\n",
    "circuit.ry('full')\n",
    "circuit.cnot('cycle')\n",
    "circuit.ry('full')\n",
    "circuit.cnot('cycle')\n",
    "circuit.ry('full')\n",
    "# print(circuit)\n",
    "\n",
    "# 定义初态和优化器\n",
    "init_state = paddle_quantum.state.zero_state(num_qubits)\n",
    "optimizer = paddle.optimizer.Adam(learning_rate=0.1, parameters=circuit.parameters())\n",
    "hamiltonian = paddle_quantum.Hamiltonian(hamiltonian_list)\n",
    "loss_func = paddle_quantum.loss.ExpecVal(hamiltonian, shots=10000)\n",
    "# 进行迭代训练\n",
    "num_itr = 80\n",
    "for itr in range(num_itr):\n",
    "    state = circuit(init_state)\n",
    "    loss = loss_func(state)\n",
    "    loss.backward()\n",
    "    optimizer.minimize(loss)\n",
    "    optimizer.clear_grad()\n",
    "    if itr % 10 == 0:\n",
    "        print(f\"The iter is {itr:3d}, loss is {loss.item():3.5f}.\")\n",
    "print(\"The theoretical value is -1.137283834485513.\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由上面的训练过程可以看出，我们可以通过量桨调用量易伏来实现量子机器学习算法。\n",
    "\n",
    "最后，我们总结一下量桨调用量易伏的用法。只需要在程序的最开头部分设置量桨和量易伏的 backend 就行。需要注意的是，很多函数都不支持在量易伏上运行，只有量子电路相关的功能才支持。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.0 ('temp')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.0"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "73730e2524c172926674de584a45f4a289689f765fd1f4813f545a2476542e53"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
